#include "AABB.h"

AABB::AABB(const AABB& box0, const AABB& box1)
{
    x = Interval(box0.x, box1.x);
    y = Interval(box0.y, box1.y);
    z = Interval(box0.z, box1.z);

    pad_to_minimums();
}

AABB::AABB(const Interval& x, const Interval& y, const Interval& z):x(x),y(y),z(z)
{
    pad_to_minimums();
}

bool AABB::hit(const Ray& r, Interval ray_t) const
{
    const Point3& ray_orig = r.origin();
    const Vec3& ray_dir = r.direction();

    for (int axis = 0; axis < 3; axis++) {
        const Interval& ax = axis_interval(axis);
        const double adinv = 1.0 / ray_dir[axis];

        auto t0 = (ax.min - ray_orig[axis]) * adinv;
        auto t1 = (ax.max - ray_orig[axis]) * adinv;

        if (t0 < t1) {
            if (t0 > ray_t.min) ray_t.min = t0;
            if (t1 < ray_t.max) ray_t.max = t1;
        }
        else {
            if (t1 > ray_t.min) ray_t.min = t1;
            if (t0 < ray_t.max) ray_t.max = t0;
        }

        if (ray_t.max <= ray_t.min)
            return false;
    }
    return true;
}

void AABB::pad_to_minimums()
{
    // Adjust the AABB so that no side is narrower than some delta, padding if necessary.

    double delta = 0.0001;
    if (x.size() < delta) x = x.expand(delta);
    if (y.size() < delta) y = y.expand(delta);
    if (z.size() < delta) z = z.expand(delta);
}

AABB operator+(const AABB& bbox, const Vec3& offset)
{
    return AABB(bbox.x + offset.x(), bbox.y + offset.y(), bbox.z + offset.z());
}

AABB operator+(const Vec3& offset, const AABB& bbox)
{
    return bbox + offset;
}
